WebWork 2 : File Upload Interceptor
This page last changed on Nov 30, 2004 by jcarreira.
We can create a nice reusable Interceptor which can hadle file uploads absolutely transparently and Action will not know anything about web-app and just gets its files:
This Interceptor may be configured to filter files with certain mime type or size, and of course to be applied to any action(s) or even included into stack. <interceptor name="fileUpload" class="neuro.util.xwork.FileUploadInterceptor"> <param name="allowedTypes">image/jpeg</param> <param name="maximumSize">8192</param> </interceptor> This removes (duplicate) web-app specific code from Action and gives ua a nice reusable component that handles 90% of all typical file upload tasks. Neat. Also illustrates power of Interceptor concept of XW:XWork/WW:WebWork. Try to do this in Struts. 8) Things to improve: error handling, reporting & i18n.//FileUploadInterceptor.java package neuro.util.xwork; import com.opensymphony.webwork.ServletActionContext; import com.opensymphony.webwork.dispatcher.multipart.MultiPartRequestWrapper; import com.opensymphony.xwork.ActionInvocation; import com.opensymphony.xwork.interceptor.Interceptor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.File; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; /** * */ public class FileUploadInterceptor implements Interceptor { protected static final Log log = LogFactory.getLog(FileUploadInterceptor.class); protected String allowedTypes; protected String disallowedTypes; protected Long maximumSize; /** */ public FileUploadInterceptor() { if (log.isDebugEnabled()) log.debug("new FileUploadInterceptor()"); } /** */ public void init() { if (log.isDebugEnabled()) log.debug("init()"); } /** */ public void destroy() { if (log.isDebugEnabled()) log.debug("destroy()"); } /** * list of allowed mime-types, optional */ public void setAllowedTypes(String allowedTypes) { this.allowedTypes = allowedTypes; } /** * list of diallowed mime-types, optional */ public void setDisallowedTypes(String disallowedTypes) { this.disallowedTypes = disallowedTypes; } /** * maximum file Size, optional */ public void setMaximumSize(Long maximumSize) { this.maximumSize = maximumSize; } /** * * TODO: i18n! */ public String intercept(ActionInvocation invocation) throws Exception { if (!(ServletActionContext.getRequest() instanceof MultiPartRequestWrapper)) { if (log.isDebugEnabled()) log.debug("bypass " + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName()); return invocation.invoke(); } MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) ServletActionContext.getRequest(); if (multiWrapper.hasErrors()) { Collection errors = multiWrapper.getErrors(); Iterator i = errors.iterator(); while (i.hasNext()) { //how to get to addError() from here? log.error((String) i.next()); } } Enumeration e = multiWrapper.getFileNames(); //Bind allowed Files while (e.hasMoreElements()) { // get the value of this input tag String inputName = (String) e.nextElement(); // get the content type String contentType = multiWrapper.getContentType(inputName); // get the name of the file from the input tag String fileName = multiWrapper.getFilesystemName(inputName); // Get a File object for the uploaded File File file = multiWrapper.getFile(inputName); log.info("file " + inputName + " " + contentType + " " + fileName + " " + file); // If it's null the upload failed if (file == null) { log.error("Error uploading: " + fileName); } else { if (acceptFile(file, contentType, inputName)) invocation.getInvocationContext().getParameters().put(inputName, file); // Do additional processing/logging... } } //Invoke Action String result = invocation.invoke(); //Cleanup e = multiWrapper.getFileNames(); while (e.hasMoreElements()) { String inputValue = (String) e.nextElement(); File file = multiWrapper.getFile(inputValue); log.info("removing file " + inputValue + " " + file); if (file != null && file.isFile()) file.delete(); } return result; } //overload this method to modify accept behaviour //TODO: addErrors? //TODO: i18n! protected boolean acceptFile(File file, String contentType, String inputName) { if (log.isDebugEnabled()) log.debug("checking" + inputName + " " + file.getName() + " " + file.length() + " " + contentType); if (maximumSize != null && maximumSize.longValue() < file.length()) log.error("file is too long:" + inputName + " " + file.getName() + " " + file.length()); else if (allowedTypes != null && allowedTypes.indexOf(contentType) < 0) log.error("Content-Type not allowed:" + inputName + " " + file.getName() + " " + contentType); else if (disallowedTypes != null && disallowedTypes.indexOf(contentType) >= 0) log.error("Content-Type disallowed:" + inputName + " " + file.getName() + " " + contentType); //somehow we need to set error messages here... else { if (log.isDebugEnabled()) log.debug("accepted"); return true; } if (log.isDebugEnabled()) log.debug("not accepted"); return false; } } Example Page code: <form .... enctype="multipart/form-data"> .... select user icon: <input type="file" name="picture"> <form> Example Action code: /** */ protected File picture; public void setPicture(File picture) { this.picture = picture; } /** */ public String execute() { if (picture != null && picture.isFile()) { final File target = new File("..."); if (target.exists()) { if (log.isDebugEnabled()) log.debug("Removed previous picture version"); target.delete(); } picture.renameTo(target); } } If this Interceptor considered generally useful - may be it will be incorporated into WW2 codebase? |
Document generated by Confluence on Dec 14, 2004 16:36 |